package online.longzipeng.cinemasys.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import online.longzipeng.cinemasys.common.utils.ExceptionUtils;
import online.longzipeng.cinemasys.config.RedisConfig;
import online.longzipeng.cinemasys.dto.session.SessionQueryResponseDTO;
import online.longzipeng.cinemasys.dto.session.SessionWebQueryResponse;
import online.longzipeng.cinemasys.entity.Movie;
import online.longzipeng.cinemasys.entity.Seat;
import online.longzipeng.cinemasys.entity.Session;
import online.longzipeng.cinemasys.entity.Type;
import online.longzipeng.cinemasys.mapper.MovieMapper;
import online.longzipeng.cinemasys.mapper.SeatMapper;
import online.longzipeng.cinemasys.mapper.SessionMapper;
import online.longzipeng.cinemasys.service.SessionService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import online.longzipeng.cinemasys.vo.session.SessionQueryVO;
import online.longzipeng.cinemasys.vo.session.SessionSaveVO;
import online.longzipeng.cinemasys.vo.session.SessionUpdateVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author lzp
 * @since 2021-04-09
 */
@Service
public class SessionServiceImpl extends ServiceImpl<SessionMapper, Session> implements SessionService {

    @Autowired
    SessionMapper sessionMapper;
    @Autowired
    MovieMapper movieMapper;
    @Autowired
    SeatMapper seatMapper;
    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public void add(Session session) {
        // 判断放这个电影的影厅20分钟内有没有电影还没放完
        QueryWrapper<Session> qwSession = new QueryWrapper<>();
        // 放电影20分钟前的时间
        LocalDateTime limitTime = session.getStartTime().plusMinutes(-20l);
        qwSession.eq("hall_id",session.getHallId()).between("end_time",limitTime,session.getStartTime());
        List<Session> sessions = sessionMapper.selectList(qwSession);
        ExceptionUtils.isTrue(sessions.size()<=0,"每个影厅的场次需要有20分钟的休息间隔！！");
        save(session);
        // 检测影院的座位设置
        setSeats();
    }

    // 查询出所有场次，检测redis如果没有键就添加座位信息
    private void setSeats() {
        // 查询开始时间大于现在的场次
        QueryWrapper<Session> qwSession = new QueryWrapper<>();
        qwSession.gt("start_time",LocalDateTime.now());
        List<Session> sessions = list(qwSession);
        /// 获取没有key的场次的id和hallId
        List<Session> sessionList = sessions.stream().filter(session -> redisTemplate.opsForValue().get(RedisConfig.SESSION_KEY + session.getId()) == null)
                .collect(Collectors.toList());
        // 生成影厅的座位放置在redis中
        sessionList.forEach(session -> {
            /// 先查出该影厅的座位
            QueryWrapper<Seat> qwSeat = new QueryWrapper<>();
            qwSeat.eq("hall_id",session.getHallId());
            List<Seat> seats = seatMapper.selectList(qwSeat);
            // 持续时间为现在到电影开始的时间长度
            Long duration = session.getStartTime().toEpochSecond(ZoneOffset.of("+8")) - LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
            redisTemplate.opsForValue().set(RedisConfig.SESSION_KEY+session.getId(), JSON.toJSONString(seats), duration,TimeUnit.SECONDS);
        });
    }

    @Override
    public void updateSession(Session session) {
        updateById(session);
    }

    @Override
    public IPage<SessionQueryResponseDTO> getListPage(SessionQueryVO queryVO) {
        Page<SessionQueryResponseDTO> sessionPage = new Page<>(queryVO.getPage(),queryVO.getSize());
        QueryWrapper<SessionQueryResponseDTO> qw = new QueryWrapper<>();
        QueryWrapper<Movie> qwMovie = new QueryWrapper<>();
        // 先判断是交集还是并集 再判断查询条件     电影名称，影厅id 电影开始时间 票价
        if (queryVO.getSearchType()) {
            qw.like(!StringUtils.isEmpty(queryVO.getPrice()),"price",queryVO.getPrice());
            // 电影开始时间所在的区间 同时有开始时间和结束时间就取中间值
            qw.between(!StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime()),
                    "start_time",queryVO.getStartTime(),queryVO.getEndTime());
            // 只有开始或只有结束就取一个值
            if (!StringUtils.isEmpty(queryVO.getStartTime())&& StringUtils.isEmpty(queryVO.getEndTime())
                    || StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime())) {
                qw.gt(!StringUtils.isEmpty(queryVO.getStartTime()),"start_time",queryVO.getStartTime());
                qw.lt(!StringUtils.isEmpty(queryVO.getEndTime()),"start_time",queryVO.getEndTime());
            }
            // 影厅id
            qw.eq(queryVO.getHallId() != null,"hall_id",queryVO.getHallId());
            // 电影名称
            if (!StringUtils.isEmpty(queryVO.getMovieName())) {
                qwMovie.like("name",queryVO.getMovieName());
                List<Long> movieIds = movieMapper.selectList(qwMovie).stream().map(Movie::getId).collect(Collectors.toList());
                qw.in("movie_id",movieIds);
            }

        }else {
            qw.or().like(!StringUtils.isEmpty(queryVO.getPrice()),"price",queryVO.getPrice());
            // 电影开始时间所在的区间 同时有开始时间和结束时间就取中间值
            qw.or().between(!StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime()),
                    "start_time",queryVO.getStartTime(),queryVO.getEndTime());
            // 只有开始或只有结束就取一个值
            if (!StringUtils.isEmpty(queryVO.getStartTime())&& StringUtils.isEmpty(queryVO.getEndTime())
                    || StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime())) {
                qw.or().gt(!StringUtils.isEmpty(queryVO.getStartTime()),"start_time",queryVO.getStartTime());
                qw.or().lt(!StringUtils.isEmpty(queryVO.getEndTime()),"start_time",queryVO.getEndTime());
            }
            // 影厅id
            qw.or().eq(queryVO.getHallId() != null,"hall_id",queryVO.getHallId());
            // 电影名称
            if (!StringUtils.isEmpty(queryVO.getMovieName())) {
                qwMovie.like("name",queryVO.getMovieName());
                List<Long> movieIds = movieMapper.selectList(qwMovie).stream().map(Movie::getId).collect(Collectors.toList());
                qw.or().in(movieIds.size()>0,"movie_id",movieIds);
            }
        }
        // 判断排序
        if ("desc".equals(queryVO.getSort())) {
            qw.orderByDesc("id");
        }else {
            qw.orderByAsc("id");
        }
        return sessionMapper.getListPage(sessionPage,qw);
    }

    @Override
    public SessionQueryResponseDTO findById(Long id) {
        return sessionMapper.findById(id);
    }

    @Override
    public SessionWebQueryResponse getList(SessionQueryVO queryVO) {
        QueryWrapper<SessionQueryResponseDTO> qw = new QueryWrapper<>();
        QueryWrapper<Movie> qwMovie = new QueryWrapper<>();
        // 先判断是交集还是并集 再判断查询条件     电影名称，影厅id 电影开始时间 票价
        if (queryVO.getSearchType()) {
            qw.like(!StringUtils.isEmpty(queryVO.getPrice()),"price",queryVO.getPrice());
            // 电影开始时间所在的区间 同时有开始时间和结束时间就取中间值
            qw.between(!StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime()),
                    "start_time",queryVO.getStartTime(),queryVO.getEndTime());
            // 只有开始或只有结束就取一个值
            if (!StringUtils.isEmpty(queryVO.getStartTime())&& StringUtils.isEmpty(queryVO.getEndTime())
                    || StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime())) {
                qw.gt(!StringUtils.isEmpty(queryVO.getStartTime()),"start_time",queryVO.getStartTime());
                qw.lt(!StringUtils.isEmpty(queryVO.getEndTime()),"start_time",queryVO.getEndTime());
            }
            // 影厅id
            qw.eq(queryVO.getHallId() != null,"hall_id",queryVO.getHallId());
            // 电影名称
            if (!StringUtils.isEmpty(queryVO.getMovieName())) {
                qwMovie.like("name",queryVO.getMovieName());
                List<Long> movieIds = movieMapper.selectList(qwMovie).stream().map(Movie::getId).collect(Collectors.toList());
                qw.in("movie_id",movieIds);
            }

        }else {
            qw.or().like(!StringUtils.isEmpty(queryVO.getPrice()),"price",queryVO.getPrice());
            // 电影开始时间所在的区间 同时有开始时间和结束时间就取中间值
            qw.or().between(!StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime()),
                    "start_time",queryVO.getStartTime(),queryVO.getEndTime());
            // 只有开始或只有结束就取一个值
            if (!StringUtils.isEmpty(queryVO.getStartTime())&& StringUtils.isEmpty(queryVO.getEndTime())
                    || StringUtils.isEmpty(queryVO.getStartTime())&& !StringUtils.isEmpty(queryVO.getEndTime())) {
                qw.or().gt(!StringUtils.isEmpty(queryVO.getStartTime()),"start_time",queryVO.getStartTime());
                qw.or().lt(!StringUtils.isEmpty(queryVO.getEndTime()),"start_time",queryVO.getEndTime());
            }
            // 影厅id
            qw.or().eq(queryVO.getHallId() != null,"hall_id",queryVO.getHallId());
            // 电影名称
            if (!StringUtils.isEmpty(queryVO.getMovieName())) {
                qwMovie.like("name",queryVO.getMovieName());
                List<Long> movieIds = movieMapper.selectList(qwMovie).stream().map(Movie::getId).collect(Collectors.toList());
                qw.or().in(movieIds.size()>0,"movie_id",movieIds);
            }
        }
        // 判断排序
        if ("desc".equals(queryVO.getSort())) {
            qw.orderByDesc("id");
        }else {
            qw.orderByAsc("id");
        }
        List<SessionQueryResponseDTO> list = sessionMapper.getList(qw);
        Map<String,List<SessionQueryResponseDTO>> map = new HashMap<>();
        SessionWebQueryResponse result = new SessionWebQueryResponse();
        for (SessionQueryResponseDTO response : list) {
            if (map.get(response.getMovie().getName()) != null) {
                map.get(response.getMovie().getName()).add(response);
            }else {
                List<SessionQueryResponseDTO> ll = new ArrayList<>();
                ll.add(response);
                map.put(response.getMovie().getName(),ll);
            }
        }
        result.setMap(map);
        return result;
    }
}
